<html>
<head>
<title>CpcPaChi - Version 0.10</title>
<style type="text/css">
span {
	font-family: 'Courier New';
	font-size: 10pt;
	color: #000000;
}
.sc0 {
	color: #808080;
}
.sc3 {
	color: #008080;
}
.sc4 {
	color: #FF8000;
}
.sc5 {
	font-weight: bold;
	color: #0000FF;
}
.sc7 {
	color: #808080;
}
.sc9 {
	color: #804000;
}
.sc10 {
	font-weight: bold;
	color: #000080;
}
.sc11 {
}
</style>
</head>
<body>
<h2>Un compilador sencillo paso a paso (10 - Variables 2)</h2>

<p align="right">(Volver al <a href="doc.html">&iacute;ndice</a>) </p>

<p>En el acercamiento anterior hab&iacute;amos comentado alguna de las ideas b&aacute;sicas del manejo de variables, y hab&iacute;amos un primer acercamiento al analizador que reconozca la declaraci&oacute;n de variables, pero todav&iacute;a no sab&iacute;amos manipularlas. Eso es lo que haremos ahora.</p>
<p>Tendremos que ser capaces de hacer cosas como: </p>
<ul>
  <li>Dar un valor a una variable: a := 1 </li>
  <li>Cambiar el valor de una variable. La primera forma que haremos, porque no supone analizar expresiones aritm&eacute;ticas, podr&iacute;a ser la operaci&oacute;n &quot;incremento&quot;: inc(a). </li>
  <li>Usar el valor de una variable, con operaciones como cpcMode(a).</li>
</ul>
<p>Como las variables se almacenan en operaciones de memoria, todas esas operaciones supondr&aacute;n leer el valor de la memoria, y, a veces, modificarlo y volverlo a guardar. En concreto, si suponemos que la variable &quot;a&quot; est&aacute; en la posici&oacute;n de memoria 10.000, esas operaciones se podr&iacute;an convertir a las siguientes secuencias en ensamblador:</p>
<p><b>a := 1</b></p>
<pre><code>ld a, 1 ; Cargar el valor 1 en el acumulador<br />
ld (10000), a ; Guardar en la posici&oacute;n 10.000 el valor del acumulador </code></pre>
<p>&nbsp;</p>
<p><b>inc(a)</b></p>
<pre><code>ld a, (10000) ; Guardar en el acumulador el valor guardado en la posici&oacute;n 10.000<br />
inc a ; Incrementar el valor del acumulador<br />
ld (10000), a ; Guardar en la posici&oacute;n 10.000 el valor del acumulador 
</code></pre>
<p>&nbsp;</p>
<p><b>cpcMode(a)</b></p>
<pre><code>ld a, (10000) ; Guardar en el acumulador el valor guardado en la posici&oacute;n 10.000<br />
call &amp;bc0e ; Cambiar modo de pantalla</code></pre>
<p>&nbsp;</p>
<p>Esto supone varios cambios en el fuente: para instrucciones como INC bastar&aacute; con a&ntilde;adir nuevas rutinas de generaci&oacute;n de c&oacute;digo, pero para &oacute;rdenes existentes como cpcMode ahora deberemos comprobar si el par&aacute;metro es una constante (y en ese caso har&iacute;amos cosas como LD A,1) o si es una variable (y entonces tomaremos el valor desde una posici&oacute;n de memoria: LD A, (10000) ). </p>
<p>Pero tambi&eacute;n hay otro cambio grande: &iquest;c&oacute;mo asignamos las direcciones de memoria para esas variables?&nbsp; &iquest;Y d&oacute;nde <b>dejamos el espacio</b> para ellas?</p>
<p>Hay varias soluciones:</p>
<ul>
  <li>La primera soluci&oacute;n, la m&aacute;s eficiente en cuanto a espacio, ser&iacute;a dejar el espacio para las variables al final del programa. Pero esta soluci&oacute;n tiene un problema: hasta que no terminamos de generar el programa, no sabemos cu&aacute;nto ocupa, de modo que no hasta entonces no sabr&iacute;amos las direcciones de las variables, por lo que deber&iacute;amos dar dos pasadas: en la primera pasada se genera todo el programa excepto las direcciones de las variables (se suele dejar indicado con una etiqueta, algo como &quot;LD A, [variableX]&quot;), y en la segunda pasada ser&aacute; cuando se escriban realmente las posiciones de memoria en lugar de estas etiquetas.</li>
  <li>La segunda soluci&oacute;n es dejar el espacio para variables al principio del programa. Ahora el inconveniente ser&aacute; parecido: como no sabemos cuantas variables hay en el programa, no tendremos claro en qu&eacute; posici&oacute;n de memoria empezar&aacute;n las &oacute;rdenes... hasta que no demos una primera pasada. Adem&aacute;s, existe un segundo riesgo: antes lanz&aacute;bamos nuestro programa resultante con algo como CALL 40000, pero ahora la primera orden ya no est&aacute; en esa posici&oacute;n, sino en otra, as&iacute; que deberemos recordar en cual, para que el cargador sea adecuado (por ejemplo, CALL 40015).</li>
  <li>Nosotros queremos esquivar (todav&iacute;a) las dos pasadas, e intentar hacerlo todo en una pasada. As&iacute; que la aproximaci&oacute;n que haremos es dar por sentado que &quot;algo&quot; ocupa una cierta cantidad de bytes como m&aacute;ximo. Por ejemplo, podr&iacute;amos suponer que el programa empieza en la posici&oacute;n de memoria 30.000, y que no ocupa m&aacute;s de 10.000 bytes, por lo que las variables podr&iacute;an estar en la posici&oacute;n de memoria 40.000. Como el tama&ntilde;o de un programa es algo muy dif&iacute;cil de prever, y la cantidad de variables no lo es tanto, quiz&aacute; fuera m&aacute;s razonable decir que las variables empiezan en la posici&oacute;n 30.000 (por ejemplo) y el programa en la 30.200. No es una aproximaci&oacute;n fiable, pero s&iacute; es sencilla, y todav&iacute;a buscamos sobre todo sencillez. Para evitar &quot;despistes&quot; en los que un CALL 30000 no accediera al programa sino al comienzo de los datos, lo que podr&iacute;a tener resultados imprevisibles, nos reservaremos los primeros 3 bytes de datos, no para variables, sino para almacenar una orden de salto a la posici&oacute;n 30200 (o a la que hayamos decidido que ser&aacute; la de comienzo del programa). </li>
</ul>
<p>Vamos a ver los cambios que todo esto supondr&iacute;a al c&oacute;digo del compilador:</p>
<p>En primer lugar, en la tabla de s&iacute;mbolos podremos guardar constantes y variables. La estructura b&aacute;sica ya etaba preparada, pero ahora cuando se guarden variables, guardaremos su direcci&oacute;n, en vez de su valor, y pasaremos a la siguiente posici&oacute;n libre:</p>
<pre><code>
listaSimbolos[numSimbolos].nombre = nombre;
listaSimbolos[numSimbolos].tamanyo = tamanyo;
listaSimbolos[numSimbolos].tipo = tipo;
if ( tipo == tipoId.CONST)  // Constante: guardo valor
  listaSimbolos[numSimbolos].valorDir = valorDir;
else if ( tipo == tipoId.VAR) // Variable: en la siguiente posici&oacute;n
{
  listaSimbolos[numSimbolos].valorDir = ultimaDireccion;
  ultimaDireccion ++; // Incremento en 1 byte, para tipo byte
}
</code></pre>


<p>Finalmente, ahora las funciones podr&aacute;n tener como par&aacute;metros constantes
num&eacute;ricas, constantes con valor, o variables, por lo que su an&aacute;lisis &quot;previo&quot; ser&aacute; algo m&aacute;s trabajoso que antes, as&iacute; que podr&iacute;amos crear una rutina espec&iacute;fica que se encargue de ello. De este modo, rutinas como la de analizar la orden PEN, que antes era as&iacute;:</p>

<pre><code>
void analizarPEN()
{
  miLexer.LeerSimbolo('(');
  string cadenaTemp = miLexer.ObtenerEnteroOIdentificador();
  int color = miTablaSimb.LeerValorOConst(cadenaTemp);
  miLexer.LeerSimbolo(')');
  miLexer.LeerSimbolo(';');
  miGenerador.genCPCPEN( color );
}
</code></pre>

<p>Pasar&aacute;n a ser simplemente as&iacute;:</p>

<pre><code>
void analizarPEN()
{
  miLexer.LeerSimbolo('(');
  miGenerador.GenLecturaValor( miLexer.ObtenerEnteroOIdentificador() );
  miLexer.LeerSimbolo(')');
  miLexer.LeerSimbolo(';');
  miGenerador.genCPCPEN();
}
</code></pre>

<p>Y esa funci&oacute;n encargada de generar la orden de lectura
del valor seg&uacute;n se trate de una constante num&eacute;rica, una constante
con nombre o una variable podr&iacute;a ser as&iacute;:</p>

<pre><code>
/* Generar la orden de lectura en la tabla de s&iacute;mbolos para una
   constante o variable, o cargar un valor directo, y finalmente
   guardarlo en el registro A */
public void GenLecturaValor(string identif)
{
  // Primero miro si es n&uacute;mero
  if ( (identif[0] &gt;= '0') &amp;&amp; (identif[0] &lt;= '9') )
  {
    int numero = Convert.ToInt32( identif );
    ficheroDestino.WriteLine(lineaActual
        + &quot; DATA 3E,&quot; + Convert.ToString(numero,16).ToUpper()
        + &quot;: ' LD A, &quot; + numero );
      lineaActual += 10; longitudTotal += 2;
  }
  // Si no es n&uacute;mero, miro si es constante
  else if (miTablaSimb.LeerTipo( identif ) == tipoId.CONST)
  {
    int valor = miTablaSimb.LeerValor( identif );
    ficheroDestino.WriteLine(lineaActual
        + &quot; DATA 3E,&quot; + Convert.ToString(valor,16).ToUpper()
        + &quot;: ' LD A, &quot; + valor + &quot; - Constante: &quot; +identif );
      lineaActual += 10; longitudTotal += 2;
  }
  // Si no es n&uacute;mero ni constante, deber&iacute;a ser variable
  else if (miTablaSimb.LeerTipo( identif ) == tipoId.VAR)
  {
    int direccion = miTablaSimb.LeerValor( identif );
    ficheroDestino.WriteLine(lineaActual
        + &quot; DATA 3A,&quot;
        + Convert.ToString(direccion % 256,16).ToUpper() +&quot;,&quot;
        + Convert.ToString(direccion / 256,16).ToUpper()
        + &quot;: ' LD A, (&quot; + direccion + &quot;) - Variable: &quot; +identif );
      lineaActual += 10; longitudTotal += 3;
  }
  // Si tampoco es variable, algo va mal
  else miLexer.ErrorFatal(&quot;Asignaci&oacute;n incorrecta&quot;);
}
</code></pre>

<p>Por supuesto, hay alg&uacute;n cambio m&aacute;s, pero de poca importancia. Por ejemplo, la orden que genera el c&oacute;digo para PEN ya no recibe el valor como par&aacute;metro, sino que da por sentado que el registro A ya se ha actualizado anteriormente.</p>


<p><br>
  Como siempre, para m&aacute;s detalles, todo el c&oacute;digo est&aacute; en la p&aacute;gina del proyecto en Google Code:<br />
  <br />
  <a href="http://code.google.com/p/cpcpachi/">http://code.google.com/p/cpcpachi/</a></p>
<p align="right">(Volver al <a href="doc.html">&iacute;ndice</a>) </p>
</body>
</html>
